using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace Jemgine.Hex
{
    public class HorizontalCoordinateSystem : CoordinateSystem
    {
        public double Radius { get; private set; }
        public double ColumnWidth { get; private set; }
        public double Width { get; private set; }
        public double HalfHeight { get; private set; }
        public double Height { get; private set; }
        public double SuperOffsetX { get; internal set; }
        public double SuperOffsetY { get; internal set; }
        public SubdivisionTechniques CreationTechnique { get; internal set; }

        public double GetRadius() { return Radius; }
        public Orientation GetOrientation() { return Orientation.Horizontal; }
        public Vector2 GetSuperOffset() { return new Vector2((float)SuperOffsetX, (float)SuperOffsetY); }

        public CoordinateSystem CreateSubSpace(SubdivisionTechniques Technique)
        {
            switch (Technique)
            {
                case SubdivisionTechniques.AlternateOrientation:
                    {
                        var Result = new VerticalCoordinateSystem(Math.Sqrt(((Radius / 2) * (Radius / 2)) / 0.75));
                        Result.SuperOffsetX = Result.HalfWidth;
                        Result.SuperOffsetY = HalfHeight - Result.Radius;
                        Result.CreationTechnique = Technique;
                        return Result;
                    }
                case SubdivisionTechniques.DivideByThree:
                    {
                        var Result = new HorizontalCoordinateSystem(Radius / 3.0);
                        Result.SuperOffsetX = Radius - Result.Radius;
                        Result.SuperOffsetY = HalfHeight - Result.HalfHeight;
                        Result.CreationTechnique = Technique;
                        return Result;
                    }
            }

            return null;
        }

        public HorizontalCoordinateSystem(double Radius)
        {
            this.Radius = Radius;
            ColumnWidth = 1.5 * Radius;
            Width = 2.0 * Radius;
            HalfHeight = Math.Sqrt((Radius * Radius) - ((Radius / 2.0) * (Radius / 2.0)));
            Height = 2.0 * HalfHeight;
        }

        public Vector2 Origin(Coordinate Coordinate)
        {
            if (Coordinate.X % 2 != 0)
                return new Vector2((float)(Coordinate.X * ColumnWidth), (float)(Coordinate.Y * Height + HalfHeight));
            else
                return new Vector2((float)(Coordinate.X * ColumnWidth), (float)(Coordinate.Y * Height));
        }

        public Vector2 Center(Coordinate Coordinate)
        {
            return Origin(Coordinate) + new Vector2((float)Radius, (float)HalfHeight);
        }
        
        public Coordinate Neighbor(Coordinate tile, Direction direction)
        {
            if (tile.X % 2 == 0)
            {
                switch (direction)
                {
                    case Direction.HorizontalNorth: return new Coordinate(tile.X, tile.Y + 1);
                    case Direction.HorizontalNorthEast: return new Coordinate(tile.X + 1, tile.Y);
                    case Direction.HorizontalSouthEast: return new Coordinate(tile.X + 1, tile.Y - 1);
                    case Direction.HorizontalSouth: return new Coordinate(tile.X, tile.Y - 1);
                    case Direction.HorizontalSouthWest: return new Coordinate(tile.X - 1, tile.Y - 1);
                    case Direction.HorizontalNorthWest: return new Coordinate(tile.X - 1, tile.Y);
                    default: throw new InvalidOperationException("Invalid direction");
                }
            }
            else //This is an odd row.
            {
                switch (direction)
                {
                    case Direction.HorizontalNorth: return new Coordinate(tile.X, tile.Y + 1);
                    case Direction.HorizontalNorthEast: return new Coordinate(tile.X + 1, tile.Y + 1);
                    case Direction.HorizontalSouthEast: return new Coordinate(tile.X + 1, tile.Y);
                    case Direction.HorizontalSouth: return new Coordinate(tile.X, tile.Y - 1);
                    case Direction.HorizontalSouthWest: return new Coordinate(tile.X - 1, tile.Y);
                    case Direction.HorizontalNorthWest: return new Coordinate(tile.X - 1, tile.Y + 1);
                    default: throw new InvalidOperationException("Invalid direction");
                }
            }

        }

        public Coordinate TileAt(Vector2 worldCoordinate)
        {
            double run = Width - ColumnWidth;
            double slope = run / HalfHeight;

            int X = (int)Math.Floor(worldCoordinate.X / ColumnWidth);
            int Y = (int)Math.Floor(worldCoordinate.Y / Height);

            double OffsetX = worldCoordinate.X - X * ColumnWidth;
            double OffsetY = worldCoordinate.Y - Y * Height;

            if (X % 2 == 0)
            {
                if (OffsetX < (-slope * OffsetY + run))
                {
                    X -= 1;
                    Y -= 1;
                }
                else if (OffsetX < (slope * OffsetY - run))
                    X -= 1;
            }
            else
            {
                //Section type B
                if (OffsetY >= HalfHeight)
                {
                    if (OffsetX < (-slope * OffsetY + run * 2.0f))
                        X -= 1;
                }
                else
                {
                    if (OffsetX < (slope * OffsetY))
                        X -= 1;
                    else
                        Y -= 1;
                }
            }

            return new Coordinate(X, Y);
        }

        public Coordinate GetSubCenter(CoordinateSystem SubSpace, Coordinate Coord)
        {
            switch (SubSpace.CreationTechnique)
            {
                case SubdivisionTechniques.AlternateOrientation:
                    if (Coord.X % 2 == 0)
                        return new Coordinate(Coord.X / 2 * 3, Coord.Y * 2);
                    else
                        return new Coordinate((Coord.X / 2 * 3) + 1, (Coord.Y * 2) + 1);
                case SubdivisionTechniques.DivideByThree:
                    if (Coord.X % 2 == 0)
                        return new Coordinate(Coord.X * 3, Coord.Y * 3);
                    else
                        return new Coordinate(Coord.X * 3, (Coord.Y * 3) + 1);
                default:
                    throw new InvalidProgramException();
            }
        }
    }

}
